﻿using System.Windows.Forms;
using System.Globalization;
using System;
using System.ComponentModel;

namespace NumericalMethod
{

    public class NumericTextBox : TextBox
   {
      bool allowSpace = false;
      bool allowRangeValidate;
      bool allowEmpty = false;
      bool allowDecimal = true;
      decimal maximumValue;
      decimal minimumValue;

      public NumericTextBox()
        :base()
      {
      }
     
      protected override void OnKeyPress( KeyPressEventArgs e )
      {
         base.OnKeyPress( e );

         NumberFormatInfo numberFormatInfo = System.Globalization.CultureInfo.CurrentCulture.NumberFormat;
         string decimalSeparator = numberFormatInfo.NumberDecimalSeparator;
         string groupSeparator = numberFormatInfo.NumberGroupSeparator;
         string negativeSign = numberFormatInfo.NegativeSign;

         string keyInput = e.KeyChar.ToString();

         if ( Char.IsDigit( e.KeyChar ) )
         {
            // Digits are OK
         }
         else if ( ( keyInput.Equals( decimalSeparator ) && allowDecimal ) 
            || keyInput.Equals( groupSeparator ) ||
          keyInput.Equals( negativeSign ) )
         {
            // Twice . or - is not allowed
            if ( ( Text.Contains( negativeSign ) && keyInput.Equals( negativeSign ) ) )
               e.Handled = true;
            else if ( ( this.Text.Contains( decimalSeparator ) && keyInput.Equals( decimalSeparator ) ) )
               e.Handled = true;
            // - must be the 1st
            else if ( SelectionStart > 0 && keyInput.Equals( negativeSign ) )
               e.Handled = true;
         }
         else if ( e.KeyChar == '\b' )
         {
            // Backspace key is OK
         }
         else if ( this.allowSpace && e.KeyChar == ' ' )
         {

         }
         else
         {
            e.Handled = true;
         }
      }

      protected override void OnLostFocus( EventArgs e )
      {
         base.OnLostFocus( e );
      }

      protected override void OnLeave( EventArgs e )
      {
         ValidateNumeric();
         base.OnLeave( e );
      }

      /// <summary>
      /// Force the field to be validated.
      /// </summary>
      public void ValidateNumeric()
      {
         if ( !allowRangeValidate )
            return;
         try
         {
            if ( DecimalValue > maximumValue || DecimalValue < minimumValue )
            {
               MessageBox.Show( String.Format( "Value must within range {0} and {1}.",
                  minimumValue, maximumValue ) );
               this.Focus();
            }
         }
         catch ( Exception )
         {
            MessageBox.Show( "Invalid character", "Numerical Method", MessageBoxButtons.OK, MessageBoxIcon.Error );
            this.Focus();
         }
      }

      public int IntValue
      {
         get
         {
            ValidateNumeric();
             if (allowEmpty && String.IsNullOrEmpty(this.Text))
                 return 0;
            return int.Parse( this.Text );
         }
      }

      /// <summary>
      /// Get Decimal Value
      /// </summary>
      public decimal DecimalValue
      {
         get
         {
             if (allowEmpty && String.IsNullOrEmpty(this.Text))
               return 0;
             decimal value;
             if (decimal.TryParse(this.Text, out value))
                 return value;
             else
             {
                 //MessageBox.Show("Invalid character", "Numerical Method", MessageBoxButtons.OK, MessageBoxIcon.Error);
                 //this.Focus();
                 return 0;
             }
         }
      }
      public double DoubleValue
      {
          get
          {
              return (double)DecimalValue;
          }
      }

      /// <summary>
      /// Set or Get the 'Allow space' in Text box
      /// </summary>
      [Description( "Set or Get the 'Allow space' in Text box" )]
      [DefaultValue( false )]
      public bool AllowSpace
      {
         set
         {
            this.allowSpace = value;
         }

         get
         {
            return this.allowSpace;
         }
      }

      /// <summary>
      /// Allow range validation the number. If exceeds or
      /// less, a popup will appear.
      /// </summary>
      [Description( "Allow range validation the number. If exceeds or less, a popup will appear." )]
      [DefaultValue( false )]
      public bool AllowRangeValidate
      {
         set
         {
            this.allowRangeValidate = value;
         }

         get
         {
            return this.allowRangeValidate;
         }
      }

      /// <summary>
      /// Set or Get Maximum decimal.
      /// </summary>
      [Description( "Set or Get Maximum decimal." )]
      [DefaultValue( 1000000000 )]
      public decimal MaximumValue
      {
         set
         {
            this.maximumValue = value;
         }

         get
         {
            return this.maximumValue;
         }
      }

      /// <summary>
      /// Set or Get Minimum decimal.
      /// </summary>
      [Description( "Set or Get Minimum decimal." )]
      [DefaultValue( 0 )]
      public decimal MinimumValue
      {
         set
         {
            this.minimumValue = value;
         }

         get
         {
            return this.minimumValue;
         }
      }

      /// <summary>
      /// Set or Get AllowEmpty value. If true, the field can
      /// be Emptied. False by default.
      /// </summary>
      [Description( "Set or Get AllowEmpty value. If true, the field can be Emptied. False by default." )]
      [DefaultValue( false )]
      public bool AllowEmpty
      {
         set
         {
            this.allowEmpty = value;
         }

         get
         {
            return this.allowEmpty;
         }
      }
      [Description( "Set or Get allowDecimal value" )]
      [DefaultValue( true )]
      public bool AllowDecimal
      {
         set
         {
            this.allowDecimal = value;
         }

         get
         {
            return this.allowDecimal;
         }
      }
   }
}